﻿//    HexagonMine
//    Copyright (c) 2009-2010 BenBear
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef __HEXAGON_MINE_GAME_H__
#define __HEXAGON_MINE_GAME_H__

#pragma once

#include <vector>

namespace hexagon_mine
{
	class CHexagonMineGame;

	enum HOLE_STATUS
	{
		HMHS_COVER,
		HMHS_MARK,
		HMHS_QUESTION,
		HMHS_OPEN,
		HMHS_BOMB,					// 游戏结束后显示真实地雷
		HMHS_BROKEN,				// 踩到地雷后的显示
	};

	struct GameInfo
	{
		int RemainMines;
		int OpenHoles;
		int TotalMines;
		int TotalHoles;
	};

	class IGameObserver
	{
		friend class CHexagonMineGame;
	protected:
		virtual void Info(const GameInfo& info) = 0;
		virtual void Finish(bool win) = 0;
	};

	class IGameShowObserver
	{
		friend class CHexagonMineGame;
	protected:
		// 设置雷坑的基本信息，包括：
		// * 在宽度、高度方向上的个数
		// * 是否是水平摆放（水平摆放则以水平为宽度；否则以垂直为宽度）
		virtual void Init(int num_width, int num_y, bool horizontal, bool left_side, bool right_side) = 0;

		struct HoleInfo
		{
			int x;					// 相对屏幕的孔 X 坐标，左上角为 0
			int y;					// 相对屏幕的孔 Y 坐标，左上角为 0
			int value;
			HOLE_STATUS status;
		};

		// 更新孔的信息
		virtual void Update(const std::vector<HoleInfo>& holes) = 0;
	};

	const int MAX_WIDTH = 13;
	const int MAX_HEIGHT = 15;
	const int MIN_PERCENT = 10;
	const int MAX_PERCENT = 30;

	class CHexagonMineGame
	{
	public:
		CHexagonMineGame();
		~CHexagonMineGame();

	private:
		IGameObserver* m_GameObserver;
		IGameShowObserver* m_ShowObserver;

	public:
		void Register(IGameObserver* observer)
		{
			m_GameObserver = observer;
		}

		void Register(IGameShowObserver* observer)
		{
			m_ShowObserver = observer;
		}

	public:
		//***********************************************
		// * 参数
		//***********************************************
		struct Config
		{
			bool RandomSize;
			bool UseDetector;
			bool HexagonShape;
			bool AutoLockMine;
			bool HasRightSideMine;
			int  Width;
			int  Height;
			int  MinePercent;
		};

	private:
		Config m_Config;

	public:
		//***********************************************
		// * 参数设置
		//***********************************************
		bool SetSize(int width, int height);

		void GetConfig(Config& config) const
		{
			config = m_Config;
		}

		bool SetConfig(Config config);

	public:
		//***********************************************
		// * 游戏控制
		//***********************************************

		// 启动游戏
		bool Start();

		enum OPERATE_TYPE
		{
			HMGO_DIG		= 0x0001,
			HMGO_MARK		= 0x0002,
			HMGO_QUESTION	= 0x0004,
			HMGO_DETECT		= 0x0008,
		};

		// 操作一个雷坑
		bool Operate(int x, int y, OPERATE_TYPE op);

		// 查询一个孔能进行的操作
		int QueryOperation(int x, int y) const;

		// 查询一个孔的显示状态
		bool QueryHoleShowStatus(int x, int y, int& value, HOLE_STATUS& status) const;

		// 查询是否还有探测针
		bool HasDetector() const;

	private:
		struct Hole
		{
			bool valid;			// 因为六边形的原因，部分雷坑可能是无效的
			int value;
			HOLE_STATUS status;
			int left_not_mine;	// 周围剩下的未打开的非雷孔的数量
		};

		std::vector<std::vector<Hole> > m_Holes;
		int m_TotalHoles;

		int m_VectorWidth;		// 二维 vector 的宽度
		int m_VectorHeight;		// 二维 vector 的高度

		int m_TotalMines;
		int m_RemainMines;
		int m_LockedMines;
		int m_OpenHoles;

		bool m_IsStarted;
		bool m_HasLaid;

		bool m_HasDetector;		// 是否还有地雷探测针

	private:
		struct Pos
		{
			int x;
			int y;
		};

		Pos HolePosToVector(Pos pos) const;
		Pos VectorPosToHole(Pos pos) const;

		void VectorPosMove(Pos& pos, int dir) const;
		void HolePosMove(Pos& pos, int dir) const;

		bool IsHoleValid(Pos hole) const;
		Hole& GetHole(Pos hole);
		const Hole& GetHole(Pos hole) const;

		bool IsHoleValidByVector(Pos vec) const;
		Hole& GetHoleByVector(Pos vec);
		const Hole& GetHoleByVector(Pos vec) const;

		int m_HoleXOffset;
		int m_HoleYOffset;

		std::vector<Pos> m_ChangedHoles;

	private:
		void CalcAndPrepareHoles();
		
		void Lay();
		void Lay(Pos vec);

		void Dig(Pos vec_pos);
		void Mark(Pos vec_pos);
		void Question(Pos vec_pos);
		void Detect(Pos vec_pos);

		bool ChangeState(Pos pos, Hole& hole, HOLE_STATUS status);
		void MarkAllMines(Pos pos);

		void Win();
		void Die(Pos vec_pos);
		void ClearShow();

		void DeepDig(Pos vec_pos);

	private:
		void NotifyInit();
		void NotifyShow();
		void NotifyShowChanged();

		void NotifyInfo();
		void NotifyFinish(bool win);
	};
}

#endif
